#ifndef LONGKEY_BASE_SCOPED_PTR_H_
#define LONGKEY_BASE_SCOPED_PTR_H_

// This is an implementation designed to match the anticipated future TR2
// implementation of the scoped_ptr class, and its closely-releated brethren,
// scoped_array, scoped_ptr_malloc

#include <assert.h>
#include <stdlib.h>
#include <cstddef>

template <class C>
class scoped_ptr
{
public:

	// The element type
	typedef C element_type;

	// Constructor, Defaults to initializing with NULL.
	// There is no way to create an uninitialized scoped_ptr.
	// The input parameter must be allocated with new.
	explicit scoped_ptr(C* p = NULL) : ptr_(p) {}

	// Destructor, If there is a C object, delete it.
	// We don't need to test ptr_ == NULL because C++ does that for us.
	~scoped_ptr()
	{
		enum {type_must_be_complete = sizeof(C) };
		delete ptr_;
	}

	// Reset. Deletes the current owned object, if any.
	// Then takes ownership of a new object, if given.
	// this->reset(this->get()) works.
	void reset(C* p = NULL)
	{
		if (p != ptr_)
		{
			enum {type_must_be_complete = sizeof(C) };
			delete ptr_;
			ptr_ = p;
		}
	}

	// Accessors to get the owned object.
	// operator* and operator-> will assert() if there is no current object.
	C& operator*() const
	{
		assert(ptr_ != NULL);
		return *ptr_;
	}
	C* operator->() const
	{
		assert(ptr_ != NULL);
		return ptr_;
	}
	C* get() const { return ptr_; }

	// Comparison operators.
	// These return whether two scoped_ptr refer to the same object, not just to
	// two different but equal objects.
	bool operator==(C* p) const { return p == ptr_; }
	bool operator!=(C* p) const { return p != ptr_; }

	// Swap two scoped pointers.
	void swap(scoped_ptr& p2)
	{
		C* tmp = ptr_;
		ptr_ = p2.ptr_;
		p2.ptr_ = tmp;
	}

	// Release a pointer.
	// The return value is the current pointer held by this object.
	// If this object holds a NULL pointer, the return value is NULL;
	// After this operation, this object will hold a NULL pointer,
	// and will not own the object any more.
	C* release()
	{
		C* retVal = ptr_;
		ptr_ = NULL;
		return retVal;
	}

private:
	C* ptr_;

	// Forbid comparison of scoped_ptr types. If C2 != C, it totally doesn't
	// make sense, and if C2 == C, it still doesn't make sense because you should
	// never have the same object owned by two different scoped_ptrs.
	template <class C2> bool operator==(scoped_ptr<C2> const& p2) const;
	template <class C2> bool operator!=(scoped_ptr<C2> const& p2) const;

	// Disallow evil constructors
	scoped_ptr(const scoped_ptr&);
	void operator=(const scoped_ptr&);
};

// Free functions
template<class C>
void swap(scoped_ptr<C>& p1, scoped_ptr<C>& p2)
{
	p1.swap(p2);
}

template <class C>
bool operator==(C* p1, const scoped_ptr<C>& p2) {
	return p1 == p2.get();
}

template <class C>
bool operator!=(C* p1, const scoped_ptr<C>& p2) {
	return p1 != p2.get();
}


// scoped_array<C> is like scoped_ptr<C>, except that the caller must allocate
// with new [] and the destructor deletes objects with delete [].
//
// As with scoped_ptr<C>, a scoped_array<C> either points to an object
// or is NULL. A scoped_array owns the object that it points to.
// scoped_array<T> is thread-compatible, and once you index into it,
// the returned objects have only the threadsafety guarantees of T.
//
// Size: sizeof(socped_array<C>) == sizeof(C*)
template <class C>
class scoped_array
{
public:

	// The element type
	typedef C element_type;

	// Constructor. Defaults to initializing with NULL.
	// There is no way to create an uninitialized scoped_array.
	// The input parameter must be allocated with new [].
	explicit scoped_array(C* p = NULL) : array_(p) { }

	// Destructor. If there is a C object, delete it.
	// We don't need to test ptr == NULL because C++ does that for us.
	~scoped_array()
	{
		enum { type_must_be_complete = sizeof(C) };
		delete[] array_;
	}

	// Reset.  Deletes the current owned object, if any.
	// Then takes ownership of a new object, if given.
	// this->reset(this->get()) works.
	void reset(C* p = NULL) {
		if (p != array_) {
			enum { type_must_be_complete = sizeof(C) };
			delete[] array_;
			array_ = p;
		}
	}

	// Get one element of the current object.
	// Will assert if there is no current object, or index i is negative.
	C& operator[](std::ptrdiff_t i) const
	{
		assert(i >= 0);
		assert(array_ != NULL);
		return array_[i];
	}

	// Get pointer to the zeroth element of the current object.
	// If there is no current object, return NULL.
	C* get() const
	{
		return array_;
	}

	// Comparison operators.
	// These return whether two scoped_array refer to the same object, not just to
	// two different but equal objects.
	bool operator==(C* p) const { return array_ == p; }
	bool operator!=(C* p) const { return array_ != p; }

	// Swap two scoped arrays.
	void swap(scoped_array& p2) {
		C* tmp = array_;
		array_ = p2.array_;
		p2.array_ = tmp;
	}

	// Release an array.
	// The return value is the current pointer held by this object.
	// If this object holds a NULL pointer, the return value is NULL.
	// After this operation, this object will hold a NULL pointer,
	// and will not own the object any more.
	C* release() {
		C* retVal = array_;
		array_ = NULL;
		return retVal;
	}

private:
	C* array_;

	// Forbid comparison of different scoped_array types.
	template <class C2> bool operator==(scoped_array<C2> const& p2) const;
	template <class C2> bool operator!=(scoped_array<C2> const& p2) const;

	// Disallow evil constructors
	scoped_array(const scoped_array&);
	void operator=(const scoped_array&);
};

// Free functions
template <class C>
void swap(scoped_array<C>& p1, scoped_array<C>& p2) {
	p1.swap(p2);
}

template <class C>
bool operator==(C* p1, const scoped_array<C>& p2) {
	return p1 == p2.get();
}

template <class C>
bool operator!=(C* p1, const scoped_array<C>& p2) {
	return p1 != p2.get();
}

// This class wraps the c library function free() in a class that can be
// passed as a template argument to scoped_ptr_malloc below.
class ScopedPtrMallocFree
{
public:
	inline void operator()(void* x) const
	{
		free(x);
	}
};

// scoped_ptr_malloc<> is similar to scoped_ptr<>, but it accepts a
// second template argument, the functor used to free the object.

template<class C, class FreeProc = ScopedPtrMallocFree>
class scoped_ptr_malloc
{
public:

	// The element type
	typedef C element_type;

	// Constructor. Defaults to initializing with NULL.
	// There is no way to create an uninitialized scoped_ptr.
	// The input parameter must be allocated with an allocator that matches the
	// Free functor. For the default Free functor, this is malloc, calloc, or realloc.
	explicit scoped_ptr_malloc(C* p = NULL) : ptr_(p) { }

	// Destructor. If there is a C object, call the Free functor.
	~scoped_ptr_malloc()
	{
		free_(ptr_);
	}

	// Reset.  Calls the Free functor on the current owned object, if any.
	// Then takes ownership of a new object, if given.
	// this->reset(this->get()) works.
	void reset(C* p = NULL) {
		if (ptr_ != p) {
			free_(ptr_);
			ptr_ = p;
		}
	}

	// Get the current object.
	// operator* and operator-> will cause an assert() failure if there is
	// no current object.
	C& operator*() const
	{
		assert(ptr_ != NULL);
		return *ptr_;
	}

	C* operator->() const
	{
		assert(ptr_ != NULL);
		return ptr_;
	}

	C* get() const
	{
		return ptr_;
	}

	// Comparison operators.
	// These return whether a scoped_ptr_malloc and a plain pointer refer
	// to the same object, not just to two different but equal objects.
	// For compatibility with the boost-derived implementation, these
	// take non-const arguments.
	bool operator==(C* p) const 
	{
		return ptr_ == p;
	}

	bool operator!=(C* p) const 
	{
		return ptr_ != p;
	}

	// Swap two scoped pointers.
	void swap(scoped_ptr_malloc & b) {
		C* tmp = b.ptr_;
		b.ptr_ = ptr_;
		ptr_ = tmp;
	}

	// Release a pointer.
	// The return value is the current pointer held by this object.
	// If this object holds a NULL pointer, the return value is NULL.
	// After this operation, this object will hold a NULL pointer,
	// and will not own the object any more.
	C* release()
	{
		C* tmp = ptr_;
		ptr_ = NULL;
		return tmp;
	}
private:
	C* ptr_;

	// no reason to use these: each scoped_ptr_malloc should have its own object
	template <class C2, class GP>
	bool operator==(scoped_ptr_malloc<C2, GP> const& p) const;
	template <class C2, class GP>
	bool operator!=(scoped_ptr_malloc<C2, GP> const& p) const;

	static FreeProc const free_;

	// Disallow evil constructors
	scoped_ptr_malloc(const scoped_ptr_malloc&);
	void operator=(const scoped_ptr_malloc&);
};

template<class C, class FP>
FP const scoped_ptr_malloc<C, FP>::free_ = FP();

template<class C, class FP> inline
void swap(scoped_ptr_malloc<C, FP>& a, scoped_ptr_malloc<C, FP>& b) {
	a.swap(b);
}

template<class C, class FP> inline
bool operator==(C* p, const scoped_ptr_malloc<C, FP>& b) {
	return p == b.get();
}

template<class C, class FP> inline
bool operator!=(C* p, const scoped_ptr_malloc<C, FP>& b) {
	return p != b.get();
}

#endif	// LONGKEY_BASE_SCOPED_PTR_H_